import OSS from 'ali-oss';
import prisma from '../lib/prisma';
import dayjs from 'dayjs';

/**
 * 数据库备份服务
 * 负责将数据库所有表的数据导出为JSON并上传到阿里云OSS
 */
export class BackupService {
  private ossClient: OSS;

  constructor() {
    // 初始化OSS客户端，使用环境变量配置
    this.ossClient = new OSS({
      accessKeyId: process.env.ALIYUN_OSS_ACCESS_KEY_ID!,
      accessKeySecret: process.env.ALIYUN_OSS_ACCESS_KEY_SECRET!,
      bucket: 'aiph-data',
      region: process.env.ALIYUN_OSS_REGION!,
    });
  }

  /**
   * 执行数据库备份
   * 将所有表的数据导出为JSON文件并上传到OSS
   */
  async backupDatabase(): Promise<void> {
    try {
      const date = dayjs().format('YYYY-MM-DD');
      const basePath = `database_backup/${date}`;
      
      console.log(`📦 开始数据库备份任务 - ${date}`);

      // 动态获取所有表名
      const tables = await this.getAllTableNames();
      console.log(`📋 发现 ${tables.length} 个表需要备份: ${tables.join(', ')}`);

      let successCount = 0;
      let failCount = 0;

      // 逐个表进行备份，避免内存占用过大
      for (const tableName of tables) {
        try {
          console.log(`🔄 正在备份表: ${tableName}`);
          
          // 获取表数据
          const data = await this.getTableData(tableName);
          
          if (data && data.length > 0) {
            // 上传到OSS
            await this.uploadToOSS(basePath, tableName, data);
            console.log(`✅ 表 ${tableName} 备份成功 (${data.length} 条记录)`);
            successCount++;
          } else {
            console.log(`ℹ️  表 ${tableName} 无数据，跳过备份`);
            successCount++;
          }
        } catch (error) {
          console.error(`❌ 表 ${tableName} 备份失败:`, error);
          failCount++;
        }
      }

      console.log(`📊 备份任务完成 - 成功: ${successCount}, 失败: ${failCount}`);
      
      if (failCount > 0) {
        throw new Error(`备份过程中有 ${failCount} 个表失败`);
      }

    } catch (error) {
      console.error('❌ 数据库备份任务失败:', error);
      throw error;
    }
  }

  /**
   * 获取数据库中所有表名
   * @returns 表名数组
   */
  private async getAllTableNames(): Promise<string[]> {
    try {
      // 使用 Prisma 的 $queryRaw 查询数据库元数据
      const result = await prisma.$queryRaw<Array<{ tablename: string }>>`
        SELECT tablename 
        FROM pg_tables 
        WHERE schemaname = 'public' 
        AND tablename NOT LIKE '_prisma_%'
        ORDER BY tablename;
      `;
      
      const tableNames = result.map(row => row.tablename);
      
      // 将数据库表名转换为 Prisma 模型名（驼峰命名）
      const prismaModelNames = tableNames.map(tableName => {
        // 将 snake_case 转换为 camelCase
        return tableName.replace(/_([a-z])/g, (match, letter) => letter.toUpperCase());
      });
      
      // 过滤出在 Prisma Client 中实际存在的模型
      const validModels = prismaModelNames.filter(modelName => {
        try {
          // 检查模型是否存在于 Prisma Client 中
          return (prisma as any)[modelName] !== undefined;
        } catch {
          return false;
        }
      });
      
      console.log(`🔍 数据库表名: ${tableNames.join(', ')}`);
      console.log(`🔍 Prisma模型名: ${prismaModelNames.join(', ')}`);
      console.log(`✅ 有效的Prisma模型: ${validModels.join(', ')}`);
      
      return validModels;
    } catch (error) {
      console.error('获取表名失败:', error);
      throw error;
    }
  }

  /**
   * 获取指定表的所有数据
   * @param tableName 表名
   * @returns 表数据数组
   */
  private async getTableData(tableName: string): Promise<any[]> {
    try {
      // 使用动态属性访问Prisma模型
      const model = (prisma as any)[tableName];
      if (!model) {
        throw new Error(`表 ${tableName} 不存在`);
      }

      // 获取所有数据
      const data = await model.findMany();
      return data;
    } catch (error) {
      console.error(`获取表 ${tableName} 数据失败:`, error);
      throw error;
    }
  }

  /**
   * 将数据上传到OSS
   * @param basePath OSS基础路径
   * @param tableName 表名
   * @param data 数据
   */
  private async uploadToOSS(basePath: string, tableName: string, data: any[]): Promise<void> {
    try {
      // 将数据序列化为JSON
      const jsonData = JSON.stringify(data, null, 2);
      
      // 生成文件名
      const fileName = `${tableName}.json`;
      const ossPath = `${basePath}/${fileName}`;

      // 上传到OSS
      const result = await this.ossClient.put(ossPath, Buffer.from(jsonData, 'utf8'), {
        headers: {
          'Content-Type': 'application/json',
        },
      });

      console.log(`📤 文件已上传到OSS: ${ossPath}`);
      
    } catch (error) {
      console.error(`上传表 ${tableName} 到OSS失败:`, error);
      throw error;
    }
  }

  /**
   * 测试OSS连接
   */
  async testOSSConnection(): Promise<boolean> {
    try {
      await this.ossClient.getBucketInfo();
      console.log('✅ OSS连接测试成功');
      return true;
    } catch (error) {
      console.error('❌ OSS连接测试失败:', error);
      return false;
    }
  }
}
